package co.fitstart.entity.coupon;

import java.beans.Transient;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import org.hibernate.annotations.DynamicUpdate;
import org.springframework.format.annotation.DateTimeFormat;

import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import co.fitstart.entity.order.Order;
import co.fitstart.entity.user.User;
import co.fitstart.entity.utils.DateFormatUtils;
import co.fitstart.entity.utils.DateUtils;


/**
 * 优惠券
 * 
 * @author Leo
 */
@Entity
@Table(name = "coupon")
@DynamicUpdate
@JsonIgnoreProperties(value={"hibernate_lazy_initializer","handler","fieldHandler"})
public class Coupon {

    @Id
    @GeneratedValue
    private Long id;

    /** 所属用户 */
    private Long userId;
    
    /** 所属用户标识  */
    private String identification;
    
    /** openid 来自用户 */
    private String openid;
    
    /** 用户姓名 */
    private String contactPerson;
    
    /** 用户微信号 */
    private String wechatNumber;

    /** 优惠券券码  */
    private String code;
    
    /** 优惠券名称 来自模版 */
    private String templateName;
    
    /** 批次名称 来自批次 */
    private String batchName;
    
    /** 实际抵扣金额 */
    private Integer amount;

    /** 优惠券类型 来自模板 */
    @Enumerated(EnumType.ORDINAL)
    private Type type;
    
    /** 券有效期 - 开始时间 来自批次 */
    @org.hibernate.annotations.Type(type = "date")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @JSONField(format = "yyyy-MM-dd")
    private Date effectiveStartDate;

    /** 券有效期 - 截止时间/过期时间 来自批次 */
    @org.hibernate.annotations.Type(type = "date")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @JSONField(format = "yyyy-MM-dd")
    private Date effectiveEndDate;

    /** 优惠券批次 */
    @ManyToOne(optional = false, fetch = FetchType.LAZY)
    @JsonIgnore
    private CouponBatch batch;

    /** 优惠券模版 来自批次 */
    @ManyToOne(optional = false, fetch = FetchType.LAZY)
    @JsonIgnore
    private CouponTemplate template;

    /** 优惠券的状态 */
    @Enumerated(EnumType.ORDINAL)
    private CouponStatus status;
    
    /** 是否为积分兑换券*/
    @Column(name = "is_credit")
    private boolean credit;
    
    /** 优惠券所属线上年卡订单 */
    @ManyToOne(optional = true, fetch = FetchType.LAZY)
    @JsonIgnore
    private Order order;
    
    /** 转赠优惠券原始用户id */
    private Long originalUserId;
    
    /** 使用时间 */
    private Date useTime;
    
    private Date createTime;
    
    /**
     * default constructor
     */
    public Coupon() {
        
    }
    
    /**
     * generate coupon according to couponBatch 
     * 
     * @param couponBatch
     */
    public Coupon(CouponBatch couponBatch) {
        this.templateName = couponBatch.getCouponTemplate().getName();
        this.batchName = couponBatch.getName();
        this.amount = couponBatch.getCouponTemplate().getAmount();
        this.type = couponBatch.getCouponTemplate().getType();
        this.effectiveStartDate = couponBatch.getEffectiveStartDate();
        this.effectiveEndDate = couponBatch.getEffectiveEndDate();
        this.batch = couponBatch;
        this.template = couponBatch.getCouponTemplate();
        this.status = CouponStatus.UNCLAIMED;
        this.credit = couponBatch.isCredit();
        this.createTime = new Date();
     }
    
    /**
     * generate coupon according to couponBatch contactPerson wechatNumber
     * 
     * @param couponBatch
     * @param contactPerson
     * @param wechatNumber
     */
    public Coupon(CouponBatch couponBatch, ExpireType expireType, String identification, String contactPerson, String wechatNumber) {
        this.templateName = couponBatch.getCouponTemplate().getName();
        this.batchName = couponBatch.getName();
        this.amount = couponBatch.getCouponTemplate().getAmount();
        this.type = couponBatch.getCouponTemplate().getType();
        this.batch = couponBatch;
        
        if (expireType != null && expireType.isFixedDate()) {
            this.effectiveStartDate = this.batch.getEffectiveStartDate();
            this.effectiveEndDate = this.batch.getEffectiveEndDate();
        }else {
            Date today = new Date();
            this.effectiveStartDate = today;
            this.effectiveEndDate = org.apache.commons.lang3.time.DateUtils.addDays(today, this.batch.getEffectiveDays());
        }
        
        this.template = couponBatch.getCouponTemplate();
        this.status = CouponStatus.UNCLAIMED;
        this.credit = couponBatch.isCredit();
        
        this.identification = identification;
        this.contactPerson = contactPerson;
        this.wechatNumber = wechatNumber;
        
        this.code = String.valueOf(System.identityHashCode(this));
        
        this.createTime = new Date();
    }
    
	/**
	 * 
	 */
	public Coupon generateCode() {
		
		this.code = ""+this.hashCode();
		
		return this;
	}
    
    /**
     * 
     * @param userId
     * @param identification
     * @return
     */
    public Coupon identify(Long userId, String identification) {
        
        this.identification = identification;
        
        // change coupon status
        this.status = CouponStatus.CLAIMED;
        
        if(this.batch.getExpireType().isFixedDate()) {
            
            this.effectiveStartDate = this.batch.getEffectiveStartDate();
            this.effectiveEndDate = this.batch.getEffectiveEndDate();
        }
        
        // if user exit, bind user & date
        if (userId != null) {
            
            this.userId = userId;
            
            if(!this.batch.getExpireType().isFixedDate()) {
                Date today = new Date();
                this.effectiveStartDate = today;
                this.effectiveEndDate = org.apache.commons.lang3.time.DateUtils.addDays(today, this.batch.getEffectiveDays());
            }
        }
        
        this.code = this.id.toString() + this.effectiveEndDate.getTime();
        
        return this;
    }
    
    /**
     * 
     * @param identification
     * @return
     */
    public Coupon identify(String identification) {
        
        this.identification = identification;
        
        // change coupon status
        this.status = CouponStatus.CLAIMED;
        
        if(this.batch.getExpireType().isFixedDate()) {
            
            this.effectiveStartDate = this.batch.getEffectiveStartDate();
            this.effectiveEndDate = this.batch.getEffectiveEndDate();
        }
        
        return this;
    }

    /**
     * 
     * @param userId
     * @return
     */
    public Coupon bindUser(Long userId) {
        this.userId = userId;
        
        // change coupon status
        this.status = CouponStatus.CLAIMED;
        
        if (this.batch.getExpireType().isFixedDate()) {
            this.effectiveStartDate = this.batch.getEffectiveStartDate();
            this.effectiveEndDate = this.batch.getEffectiveEndDate();
        }else {
            Date today = new Date();
            this.effectiveStartDate = today;
            this.effectiveEndDate = org.apache.commons.lang3.time.DateUtils.addDays(today, this.batch.getEffectiveDays());
        }
        
        return this;
    }
    
    /**
     * 
     * @return
     */
    public Coupon claimed() {
        this.status = CouponStatus.CLAIMED;
        return this;
    }
    
    /**
     * 
     * @return
     */
    public Coupon occupy() {
        this.status = CouponStatus.OCCUPIED;
        return this;
    }
    
    /**
     * 
     * @return
     */
    public Coupon use() {
        this.status = CouponStatus.USED;
        this.batch.setUsedAmount(this.batch.getUsedAmount() + 1);
        this.useTime = new Date();
        return this;
    }

    /**
     * 优惠券过期
     * @return
     */
    public Coupon expiry() {
        this.status = CouponStatus.OUT_OFF_DATE;
        return this;
    }
    
    /**
     * 
     * @param from
     * @param to
     * @return
     */
    public Coupon give(User from, User to) {
        this.userId = to.getId();
        this.identification = to.getMobile();
        this.openid = to.getOpenid();
        this.contactPerson = to.getName();
        this.wechatNumber = to.getWechat() == null ? null : to.getWechat().getCode();
        this.originalUserId = from.getId();
        return this;
    }
    
    /**
     * 
     * @param from
     * @param name
     * @param mobile
     * @return
     */
    public Coupon give(User from, String name, String mobile) {
        this.userId = null;
        this.identification = mobile;
        this.contactPerson = name;
        this.status = CouponStatus.UNCLAIMED;
        this.originalUserId = from.getId();
        return this;
    }
    
    @Transient
    public boolean couldUse() {
        Date today = new Date();
        if(this.status.equals(CouponStatus.OUT_OFF_DATE) || 
           this.status.equals(CouponStatus.HOLD) || 
           this.status.equals(CouponStatus.USED) || 
           this.status.equals(CouponStatus.OCCUPIED) ||
           this.status.equals(CouponStatus.UNCLAIMED) || 
           this.effectiveStartDate.after(today) || this.effectiveEndDate.before(today)) {
            return false;
        }
        return true;
    }
    
    /**
     * 
     * @return
     */
    @Transient
    public boolean isAvaliable() {
        Date today = new Date();
        if(this.status.equals(CouponStatus.OUT_OFF_DATE) || this.effectiveStartDate.after(today) || this.effectiveEndDate.before(today)) {
            return false;
        }
        return true;
    }
    
    /**
     * Get rest days when coupon is effective
     *  
     * @return
     */
    @JSONField(serialize = false)
    public int getRestDay() {
        Date today = DateUtils.parseDate(DateFormatUtils.format(new Date(), "yyyy-MM-dd"), "yyyy-MM-dd");
        return (this.effectiveEndDate == null || today.after(this.effectiveEndDate)) ? -1 : (DateUtils.daysBetween(this.effectiveEndDate, DateUtils.parseDate(DateFormatUtils.format(new Date(), "yyyy-MM-dd"), "yyyy-MM-dd")) + 1);
    }

    /**
     * 
     * @return
     */
    public String getFormatEffectiveStartDate() {
        return DateFormatUtils.format(this.effectiveStartDate, "yyyy-MM-dd");
    }
    
    /**
     * 
     * @return
     */
    public String getFormatEffectiveEndDate() {
        return DateFormatUtils.format(this.effectiveEndDate, "yyyy-MM-dd");
    }

    /**
     * 
     * @return
     */
    public DiscountType getDiscountType() {
        return this.getTemplate().getDiscountType();
    }

    /**
     * 
     * @return
     */
    public String getDescription() {
        return this.getBatch().getCouponTemplate().getDescription();
    }

    /**
     * 
     * @return
     */
    public boolean isGiveAvailable() {
        return this.getBatch().getCouponTemplate().isGiveAvailable();
    }

    /**
     * 
     * @return
     */
    public String getStatusDescription() {
        return this.status == null ? null : this.status.getDescription();
    }

    /**
     * @return the id
     */
    public Long getId() {
        return id;
    }

    /**
     * @return the userId
     */
    public Long getUserId() {
        return userId;
    }

    /**
     * @return the identification
     */
    public String getIdentification() {
        return identification;
    }

    /**
     * @return the openid
     */
    public String getOpenid() {
        return openid;
    }

    /**
     * @return the contactPerson
     */
    public String getContactPerson() {
        return contactPerson;
    }

    /**
     * @return the wechatNumber
     */
    public String getWechatNumber() {
        return wechatNumber;
    }

    /**
     * @return the code
     */
    public String getCode() {
        return code;
    }

    /**
     * @return the templateName
     */
    public String getTemplateName() {
        return templateName;
    }

    /**
     * @return the batchName
     */
    public String getBatchName() {
        return batchName;
    }

    /**
     * @return the amount
     */
    public Integer getAmount() {
        return amount;
    }

    /**
     * @return the type
     */
    public Type getType() {
        return type;
    }

    /**
     * @return the effectiveStartDate
     */
    public Date getEffectiveStartDate() {
        return effectiveStartDate;
    }

    /**
     * @return the effectiveEndDate
     */
    public Date getEffectiveEndDate() {
        return effectiveEndDate;
    }

    /**
     * @return the batch
     */
    public CouponBatch getBatch() {
        return batch;
    }

    /**
     * @return the template
     */
    public CouponTemplate getTemplate() {
        return template;
    }

    /**
     * @return the status
     */
    public CouponStatus getStatus() {
        return status;
    }

    /**
     * @return the credit
     */
    public boolean isCredit() {
        return credit;
    }

    /**
     * @return the order
     */
    public Order getOrder() {
        return order;
    }

    /**
     * @return the originalUserId
     */
    public Long getOriginalUserId() {
        return originalUserId;
    }

    /**
     * @return the useTime
     */
    public Date getUseTime() {
        return useTime;
    }
    
    /**
	 * @return the createTime
	 */
	public Date getCreateTime() {
		return createTime;
	}

    /**
     * @param id the id to set
     */
    public void setId(Long id) {
        this.id = id;
    }

    /**
     * @param userId the userId to set
     */
    public void setUserId(Long userId) {
        this.userId = userId;
    }

    /**
     * @param identification the identification to set
     */
    public void setIdentification(String identification) {
        this.identification = identification;
    }

    /**
     * @param openid the openid to set
     */
    public void setOpenid(String openid) {
        this.openid = openid;
    }

    /**
     * @param contactPerson the contactPerson to set
     */
    public void setContactPerson(String contactPerson) {
        this.contactPerson = contactPerson;
    }

    /**
     * @param wechatNumber the wechatNumber to set
     */
    public void setWechatNumber(String wechatNumber) {
        this.wechatNumber = wechatNumber;
    }

    /**
     * @param code the code to set
     */
    public void setCode(String code) {
        this.code = code;
    }

    /**
     * @param templateName the templateName to set
     */
    public void setTemplateName(String templateName) {
        this.templateName = templateName;
    }

    /**
     * @param batchName the batchName to set
     */
    public void setBatchName(String batchName) {
        this.batchName = batchName;
    }

    /**
     * @param amount the amount to set
     */
    public void setAmount(Integer amount) {
        this.amount = amount;
    }

    /**
     * @param type the type to set
     */
    public void setType(Type type) {
        this.type = type;
    }

    /**
     * @param effectiveStartDate the effectiveStartDate to set
     */
    public void setEffectiveStartDate(Date effectiveStartDate) {
        this.effectiveStartDate = effectiveStartDate;
    }

    /**
     * @param effectiveEndDate the effectiveEndDate to set
     */
    public void setEffectiveEndDate(Date effectiveEndDate) {
        this.effectiveEndDate = effectiveEndDate;
    }

    /**
     * @param batch the batch to set
     */
    public void setBatch(CouponBatch batch) {
        this.batch = batch;
    }

    /**
     * @param template the template to set
     */
    public void setTemplate(CouponTemplate template) {
        this.template = template;
    }

    /**
     * @param status the status to set
     */
    public void setStatus(CouponStatus status) {
        this.status = status;
    }

    /**
     * @param credit the credit to set
     */
    public void setCredit(boolean credit) {
        this.credit = credit;
    }

    /**
     * @param order the order to set
     */
    public void setOrder(Order order) {
        this.order = order;
    }

    /**
     * @param originalUserId the originalUserId to set
     */
    public void setOriginalUserId(Long originalUserId) {
        this.originalUserId = originalUserId;
    }

    /**
     * @param useTime the useTime to set
     */
    public void setUseTime(Date useTime) {
        this.useTime = useTime;
    }
    
    /**
	 * @param createTime the createTime to set
	 */
	public void setCreateTime(Date createTime) {
		this.createTime = createTime;
	}
}
